#include <algorithm>
#include <cstring>
#include <iostream>
#include <mutex>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
#include <unordered_map>
#include <vector>

#define BUFFER_SIZE 1024 // 缓冲区大小
#define SELECT_TIMEOUT 3 // 1秒超时

using namespace std;

class ServiceManager {
private:
	std::unordered_map<int, int> fd2port; // 文件描述符到端口号的映射
	std::unordered_map<int, int> port2fd; // 端口号到文件描述符的映射
	std::mutex maps_mutex;                // 保护映射表的互斥锁
	std::atomic<bool> running{false};     // 工作线程运行标志
	std::thread worker_thread;            // 工作线程

	// select表示等待文件描述符集中的任何一个文件描述符变为可读或可写，如果没有文件描述符就绪，则阻塞等待，直到超时。
	void worker() {
		// 工作线程会在循环中不断地等待文件描述符就绪或超时，并处理就绪的文件描述符。
		// 当工作线程被销毁时，running标志会被设置为false，从而导致select返回-1，从而退出循环。
		while (running) {
			std::cout << "\n=== scan all service ===" << std::endl;
			fd_set read_fds;    // 用于存储所有监听的文件描述符
			FD_ZERO(&read_fds); // 清空文件描述符集
			int max_fd = 0;     // 用于存储最大的文件描述符, 初始化为0
			{                   // 遍历所有服务并添加到文件描述符集中
				std::lock_guard<std::mutex> lock(maps_mutex);
				for (const auto &pair: fd2port) {
					std::cout << "=>  fd: " << pair.first << ", port: " << pair.second << std::endl;
					int fd = pair.first;
					FD_SET(fd, &read_fds);
					if (fd > max_fd) max_fd = fd; // 更新最大文件描述符
				}
			}

			// 超时时间设置为1秒，tv_usec设置为0表示不等待微秒部分
			struct timeval tv{.tv_sec = SELECT_TIMEOUT, .tv_usec = 0};
			int activity = select(max_fd + 1, &read_fds, nullptr, nullptr, &tv);
			// 如果select返回-1，并且错误码不是EINTR（表示被信号中断），则打印错误信息并退出循环
			if (activity < 0 && errno != EINTR) break;

			// 处理活跃socket
			std::vector<int> ready_fds;
			{
				std::lock_guard<std::mutex> lock(maps_mutex);
				for (const auto &pair: fd2port) {
					// 检查文件描述符是否在集合中
					if (FD_ISSET(pair.first, &read_fds)) {
						ready_fds.push_back(pair.first);
					}
				}
			}

			for (int fd: ready_fds) {
				handleData(fd);
			}
		}
	}

	void handleData(int sockfd) {
		char buffer[BUFFER_SIZE];
		struct sockaddr_in client_addr{};
		socklen_t addr_len = sizeof(client_addr);
		ssize_t recv_len   = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &addr_len);
		if (recv_len < 0) {
			std::cerr << "Error receiving data on fd: " << sockfd << std::endl;
			destroyByFd(sockfd);
		} else {
			// 这里添加实际业务处理逻辑
			std::cout << "\nReceived " << recv_len << " bytes on port: " << fd2port[sockfd] << std::endl;
		}
	}

	void destroyByFd(int sockfd) {
		std::lock_guard<std::mutex> lock(maps_mutex);
		if (fd2port.count(sockfd)) {
			int port = fd2port[sockfd];
			port2fd.erase(port);
			fd2port.erase(sockfd);
			close(sockfd);
			std::cout << "Service closed on port: " << port << std::endl;
		}
	}

public:
	ServiceManager() {
		worker_thread = std::thread(&ServiceManager::worker, this);
		running       = true;
	}

	~ServiceManager() {
		running = false;
		if (worker_thread.joinable()) {
			worker_thread.join();
		}

		std::lock_guard<std::mutex> lock(maps_mutex);
		for (const auto &pair: fd2port) {
			close(pair.first);
		}
		fd2port.clear();
		port2fd.clear();
	}

	int createService() {
		int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
		if (sockfd < 0) return -1;

		struct sockaddr_in server_addr{};
		memset(&server_addr, 0, sizeof(server_addr));
		server_addr.sin_family      = AF_INET;
		server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		server_addr.sin_port        = htons(0);
		if (::bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
			close(sockfd);
			return -1;
		}

		// 获取实际分配的端口
		socklen_t len = sizeof(server_addr);
		getsockname(sockfd, (struct sockaddr *) &server_addr, &len);
		int port = ntohs(server_addr.sin_port);

		std::lock_guard<std::mutex> lock(maps_mutex);
		fd2port[sockfd] = port;
		port2fd[port]   = sockfd;
		return port;
	}

	void destroyService(int port) {
		std::lock_guard<std::mutex> lock(maps_mutex);
		if (port2fd.count(port)) {
			int sockfd = port2fd[port];
			port2fd.erase(port);
			fd2port.erase(sockfd);
			close(sockfd);
		}
	}

	// 禁用拷贝和移动
	ServiceManager(const ServiceManager &)            = delete;
	ServiceManager &operator=(const ServiceManager &) = delete;
};


// g++ -std=c++11 -pthread -o multi_udp_select.exe multi_udp_select.cpp
int main() {
	ServiceManager manager;
	int port1 = manager.createService();
	int port2 = manager.createService();

	std::cout << "Created services on ports: " << port1 << ", " << port2 << std::endl;

	// 保持程序运行一段时间，以便测试服务销毁
	getchar();

	manager.destroyService(port1);
	manager.destroyService(port2);
	return 0;
}
